//OBJLoader.h
//Created 04/12/13
//Created By Daniel Bowler
//
//Loads .obj and .mtl (material) files. It will interact with a StaticModel
//and create the VB, IB and create the vector of materials. 
//
//Will work with instanced static models too because that has a static model
//instance. 
//
//Note: Not all .objs will work. They MUST have normal data for example. 

#pragma once

//XNA Maths library
#include "d3dUtil.h"
//So we have StaticModel::TessellationData structure. 
#include "StaticModel.h"
//So we can have per vertex structures. 
#include "TileForwardNormalMapFX.h"
#include "TileForwardTextureMapFX.h"
#include "TileForwardSolidColourFX.h"

class OBJLoader
{
public:
	//Constructor and destructor. Use OpenOBJ. 
	OBJLoader();
	~OBJLoader();

	//Entery function to the OBJ parser. Pass the StaticModel instance that
	//calls this funciton (this) and the OBJLoader will access and set the
	//private data.
	void LoadOBJ(ID3D11Device* device,             //For buffer loading etc. 
		StaticModel* mod,						   //OBJ Loader will init vb, ib and materials.
		LPCWSTR objFilePath,					   //.obj file path inc .obj
		std::vector<LPCWSTR> &objMaterialFilePath, //Array of .mtl file paths
		std::string textureFolderPath,			   //File path of where the textures are located
		bool useDispMapping,					   //Are we going to render this with disp mapping where possible?
		TessellationData* tessData,				   //If useDispMapping == true, this sets tessellation factors
		XMFLOAT2 texScale);						   //Texture scaling.

private:
	//Supported compoents of the material. Fill one of these out per newmtl
	//and use this to create a new StaticModel::Material struct (Later)
	struct OBJMaterial
	{
		OBJMaterial()
		{
			MaterialName = "";
			Ka = XMFLOAT3(1.0f, 1.0f, 1.0f);
			Kd = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
			Ks = XMFLOAT4(1.0f, 1.0f, 1.0f, 64.0f);

			DiffuseMapName = "";
			NormalMapName = "";

			UseDiffuseMap = false;
			UseNormalMap = false;
		}

		//Material name.
		std::string MaterialName;

		//Material Colour 
		XMFLOAT3 Ka;
		XMFLOAT4 Kd; //.a is transparancy (d)
		XMFLOAT4 Ks; //.a is specular power. (Ns)

		//diffuse/normal map filename (Not including texture folder name)
		std::string DiffuseMapName;
		std::string NormalMapName;

		//Flags indicating if diffuse/normal map to be used - true when one is decalred
		//in the material. 
		bool UseDiffuseMap;
		bool UseNormalMap;
	};

	//Private structure that represents the data for one submodel. This is a temp structure filled
	//as we call ParseSubModel(). This will be used to copy data back to the material
	//vector of the StaticModel passed to the LoadOBJ() function. 
	struct OBJSubModel
	{
		OBJSubModel()
		{
			//Do NOT release in the constructor. Copying the address
			//will not increment the reference count. Therefore, if you
			//release the resource, you will delete it. 
			//
			//This is kind of a temp structure that we will as we read through the
			//file. Afterwards, we will copy this across to the StaticModel that
			//has been loaded.
			VB = 0;
			IB = 0;
			TextureMap = 0;
			NormalMap = 0;

			VertexStart = 0;
			VertexCount = 0;

			ObjMaterial = 0;
		}

		//Vertex buffer and index buffer
		ID3D11Buffer* VB;
		ID3D11Buffer* IB;

		//Shader Resources
		ID3D11ShaderResourceView* TextureMap;
		ID3D11ShaderResourceView* NormalMap;

		//OBJMaterial.
		OBJMaterial* ObjMaterial;

		//Vertex start and count. Start will always be 0
		//as we have a new vertex buffer per submodel (Somewhat inefficient, but
		//its just a tad easier this way)
		unsigned VertexStart;
		unsigned VertexCount;
	};

private:
	//Private functions to parse the file. 
	//
	//Entery function which parses a single material file
	void ParseMaterialMain(LPCWSTR objMaterialFilePath);
	//Entry function which parses the .obj file - vertex and index data
	void ParseOBJMain(LPCWSTR objFilePath, std::string texFile, ID3D11Device* device);


	//Called when newmtl is found inside the .mtl file. Fills a new OBJMaterial
	//struct and pushes on to vector. 
	void LoadNewMaterial(std::ifstream &fin);

	//Called when parsing .obj file (vertex/index). 
	void ParseVertexPosition(std::ifstream &fin);
	void ParseVertexTex(std::ifstream &fin);
	void ParseVertexNormal(std::ifstream &fin);
	void ParseSubModel(std::ifstream &fin, std::string texFile, ID3D11Device* device);

	//Parses an individual face. (f 34/55/32 2232/34/44 23/232/11). Called when f is found after
	//calling ParseSubModel().
	void ParseFace(std::ifstream &fin, OBJMaterial* objM, 
		std::vector<XMFLOAT3>&subModelPositions,
		std::vector<XMFLOAT2>&subModelTex,
		std::vector<XMFLOAT3>&subModelNorm);

	//Creates OBJSubModel
	void LoadNewOBJSubModel(ID3D11Device* device, OBJMaterial* m, std::string texFile,
		std::vector<XMFLOAT3>&subModelPositions,
		std::vector<XMFLOAT2>&subModelTex,
		std::vector<XMFLOAT3>&subModelNorm,
		std::vector<XMFLOAT4>&subModelTang);

	//Extracts vertex position, tex coord and normal vector
	//from a string. Called once per vertex in the ParseSubModel
	//function once we have read the vertex data in to a seperate string.
	void ExtractVertexData(std::string &str, int &posIndex, int &texIndex, int &normIndex);

	//Function that generates a normal or tangent vector. 
	XMFLOAT3 GenNormalVector(XMFLOAT3 a, XMFLOAT3 b, XMFLOAT3 c);
	//Much credit to: Pg 184/5 of Mathematics For 3D Game Programming and Compute
	//Graphics 3rd Edition.
	void GenTangentVectors(std::vector<XMFLOAT3>&pos,
		std::vector<XMFLOAT3>&norm, 
		std::vector<XMFLOAT2>&tex, 
		std::vector<XMFLOAT4>&tangOut, 
		long vertexCount);

private:
	//Skips the line. 
	void SkipLine(std::ifstream &fin);
	//Skips white space
	void SkipWhiteSpace(std::ifstream &fin);

	//DEBUG CODE:
	//
	//Outputs a single char, strings, ints to the debug box. 
	void OutputChar(char c, bool newLine = false);
	void OutputString(std::string str, bool newLine = false);
	void OutputInt(int i, bool newLine = false);
	void OutputFloat(float f, bool newLine = false);

	//HELPER CODE:
	//
	//Converts from std::string to std::wstring
	std::wstring ToWS(std::string s);

private:
	//Vector of OBJMaterials (one per LoadNewMaterial call)
	std::vector<OBJMaterial>objMats;
	//Vector of OBJSubModels (One per ParseSubModel call)
	std::vector<OBJSubModel>objSubModels;

	//Data read in from the .obj file - every vertex, tex coord and normal vector
	//from the file:
	//
	//Vector of positions. XMFLOAT3 - we would ignore the 4th if it 
	//exists. New entry per ParseVertexPosition() call
	std::vector<XMFLOAT3>vertPos;
	//Vector of Texcoords. One new entry per ParseVertexTex() call.
	std::vector<XMFLOAT2>vertTex;
	//Vector of normal vectors. One new entry per ParseVertexNormal call
	std::vector<XMFLOAT3>vertNorm;
};
